#ifndef TCP_CONNECTION_H
#define TCP_CONNECTION_H

#include "Common.h"
#include "Buffer.h"
#include "EventLoop.h"
// #include "TcpServer.h"
#include <memory>
#include <functional>
#include "Callbacks.h"



class TcpConnection : noncopyable,
					public std::enable_shared_from_this<TcpConnection> {  

public:
	TcpConnection(int conn_fd, EventLoop *loopPtr):
		loop(loopPtr),
		channel(new Channel(conn_fd, EVENT_READ, loop)),
		input(new Buffer()),
		output(new Buffer()),
		name("connection-" + std::to_string(conn_fd)) {
	}

	~TcpConnection() {
	}

	void handleChannel(void) {
	    channel->setEventReadCallBack(std::bind(&TcpConnection::handleRead, this));
	    channel->setEventWriteCallBack(std::bind(&TcpConnection::handleWrite, this));
	    channel->add(channel->fd);
	}

	int sendData(void *data, int size) {
		size_t writed_bytes = 0;
		size_t left_bytes = size;
		printf("start to send data\n");

		int fault = 0;

		if (output->getReadableSize() == 0) {

		// if (!channel->writeEventIsEnable() && output->getReadableSize() == 0) {
			printf("directly write\n");
			if (channel == NULL) {
				printf("channel is null\n");
			}
			printf("channel->fd is %d\n", channel->fd);
			writed_bytes = write(channel->fd, data, size);
			if (writed_bytes >= 0) {
				left_bytes -= writed_bytes;
			} else {
				writed_bytes = 0;

				if (errno != EWOULDBLOCK) {
					if (errno == EPIPE || errno == ECONNRESET)
						fault = 1;
				}
			}
		}

		if (!fault && left_bytes > 0) {
			printf("undirectly write\n");
			output->doAppend((data + writed_bytes), left_bytes);

			if (!channel->writeEventIsEnable()) {
				channel->writeEventEnable();
				// channel->writeEventEnable(true);
			}
		}
		printf("end to send data\n");
		return writed_bytes;
	}	



  	void setConnCompleteCallBack(const ConnCompleteCallBack& cb) { 
  		connCompleteCallBack = cb; 
  	}

  	void setConCloseCallBack(const ConnCloseCallBack &cb) {
  		connCloseCallBack = cb;
  	}

  	void setMessageCallBack(const MessageCallBack &cb) {
  		messageCallBack = cb;
  	}

  	void setWriteCompleteCallBack(const WriteCompleteCallBack &cb) {
		writeCompleteCallBack = cb;  		
  	}



	int sendBuffer(Buffer *buffer) {
		int send_size = buffer->getReadableSize();
		int rt = sendData(buffer->data_ + buffer->readIndex_, send_size);
		buffer->readIndex_ += send_size;
		return rt;
	}


	int handleClose(void) {
		channel->remove(channel->fd);
	 	// loop->removeChannelEvent(channel->fd, channel);
		
		if (connCloseCallBack != NULL) {
			printf("connCloseCallBack start\n");
			connCloseCallBack(ptr);
			// connCloseCallBack(shared_from_this());
			// TcpConnectionPtr ptr(shared_from_this());
			// connCloseCallBack(this);
			printf("connCloseCallBack end\n");
		}	

	}

	int handleRead() {
		printf("conn hanndle read start fd %d\n", channel->fd);
		if (input->doSocketRead(channel->fd) > 0) {

			if (messageCallBack != NULL) {
				printf("messageCallBack start\n");
				messageCallBack(input, ptr);
				printf("messageCallBack end\n");
			}
		} else {
			handleClose();
		}
		printf("conn hanndle read end\n");
	}


	int handleWrite() {
		size_t writed_bytes = write(channel->fd, output->data_ + output->readIndex_,
								output->getReadableSize());
		if (writed_bytes > 0) {
			output->readIndex_ += writed_bytes;
			if (output->getReadableSize() == 0) {
				channel->writeEventDisable();
			}
			
			if (writeCompleteCallBack != NULL) {
				writeCompleteCallBack(ptr);
			}

		} else {
	        printf("handle_write for tcp connection %s", name.c_str());
	    }

	}


	int shutDown() {
		if (shutdown(channel->fd, SHUT_WR) < 0) {
			printf("tcp_connection_shutdown failed, socket == %d", channel->fd);
		}
	}


	int connectEstablished() {
		if (connCompleteCallBack != NULL) {
			channel->tie(shared_from_this());
			ptr = shared_from_this();
			connCompleteCallBack(ptr);
			handleChannel();
		}

	}

public:
	EventLoop *loop;
	// std::unique_ptr<Channel> channel;
	Channel *channel;
	std::string name;
	Buffer *input;
	Buffer *output;
	void *data;
	void *request;
	void *response;
	TcpConnectionPtr ptr;
	ConnCompleteCallBack connCompleteCallBack;
	MessageCallBack messageCallBack;	
	WriteCompleteCallBack writeCompleteCallBack;
	ConnCloseCallBack connCloseCallBack;
};

#endif




// 这是我改造的c++版本,目前还在调试中
// https://gitee.com/willam-dialogue/net_reactor


// 调试过程中遇到了几个问题:
// 1.在telnet以后, 客户端第一次发送消息,可以正常收到消息. 客户端第二次发送消息,会导致server coredump. 目前初步定位到问题是发生在TcpConnection.h中的sendData函数,更具体的原因没有找到

// 2.如果将回调函数注册为如下方式:
// TcpConnection 公有云继承了enabled_shared_from_this
// typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
// typedef std::function<void (const TcpConnectionPtr &)>  ConnCompleteCallBack;
// typedef std::function<void (const TcpConnectionPtr &)>   ConnCloseCallBack;
// typedef std::function<void (const TcpConnectionPtr &)>   WriteCompleteCallBack;
// typedef std::function<void (Buffer*, const TcpConnectionPtr &)>  MessageCallBack;

// 在TcpConnection调用ConnCompleteCallBack就没有问题.

// 但是在channel中绑定了TcpConnection.h 中的handleRead和handleWrite的回调,在调试过程中会报weak_ptr的相关错误.实际定位发现在handleRead里面调用了MessageCallBack的回调,MessageCallBack的入参是shared_from_this()，weak_ptr的错误由这个产生的，目前还在看是否因为使用方法的原因引起的.

// 希望同学们能够一起帮忙看看 这些问题, 我还没找到好的方法
